In   -
Out  ZapJRF
Type Module
Max  64k
Ver  1.23b

Define Workspace
 Name    ECursors
 Default r10
  c_wind      !   Current window offset
  c_line      !   Current line cursor is on.
  c_col       !   Current column cursor is on (including margin).
  c_off       !   Current file offset of cursor.
  c_loff      !   Current file offset of start of physical line of cursor.
  c_width     !   Width of cursor in characters.
  c_owind     !   Window of old cursor position.
  c_oline     !   Line of old cursor position.
  c_ocol      !   Column of old cursor position.
  c_owidth    !   Old cursor width.

 Name    EFile
 Default r9
  f_ptr       !   pointer to file buffer / -1
  f_bufl      !   length of file buffer
  f_len       !   length of file
  f_name      !   pointer to filename
  f_load      !   load address of file
  f_exec      !   exec address of file
  f_flags     !   file flags
  f_uptr      !   pointer to undo buffer
  f_ubufl     !   length of undo buffer
  f_ulen      !   length of data in undo buffer
  f_undo      !   offset of main undo pointer
  f_undop     !   offset of undo block sub-pointer
  f_blklen    !   total length of file block + undo
  f_splito    !   offset in file of start of split
  f_splite    !   offset in buf of end of split
  f_splits    !   size of split=splite-splito
  f_mptr      !   pointer to marker buffer
  f_mbufl     !   length of marker buffer
  f_mlen      !   length of data in marker buffer
  f_mark      !   current offset in marker buffer
  f_docom     !   current command being 'done'.
  f_source    !   word for use of f_cmode mode
  f_dolen     !   length of insertion/deletion
  f_dodata    !   data to be inserted/replace with.
  f_altered   !   first altered offset in file /-1
  f_shiftable !   first shiftable offset in file /-1
  f_change    !   signed change of shiftable offset
  f_depth     !   multiple operation depth / 0
  f_links     !   v links to other files
  f_cmode     !   mode which 'owns' file / -1
  f_end_mark  !   end of list marker

 Name    EWindow
 Default r8
  w_handle    !   window handle / -1 if deleted
  w_minx      !   min scn x
  w_miny      !   min scn y
  w_maxx      !   max scn x
  w_maxy      !   max scn y
  w_scrollx   !   x scroll
  w_scrolly   !   y scroll
  w_infront   !   window handle of window in front
  w_windowflags ! wimp window flags
  w_file      !   offset of file showing
  w_format    !   display format
  w_width     !   width of work area in chars
  w_height    !   height of work area in chars
  w_txt       !   pointer to cached text buffer
  w_txth      !   height of cached txt buf in lines
  w_txtw      !   char width of cached line (mult 8)
  w_txtn      !   number of lines cached
  w_coff      !   off in text of first cached line
  w_cline     !   line number of first cached line
  w_clogl     !   log line num of first cached line
  w_tab       !   coltab width
  w_tabc      !   actual tab character code (x4)
  w_cr        !   code of return/line feed
  w_flags     !   (my) window flags
  w_title     !   pointer to window title buffer
  w_bpl       !   bytes per line / width of text
  w_margin    !   width of margin (inc line nos)
  w_rl        !   height in lines of each character
  w_rw        !   width in pixels of each character
  w_rh        !   text height in pixels (no spacing)
  w_addr      !   address of first byte in display
  w_stline    !   line number of first line in disp
  w_titlelen  !   title length (excluding 0)
  w_titbuf    !   address of title showing buffer
  w_titlen    !   length of title showing string
  w_mwidth    !   user required margin width
  w_tabchar   !   user required tab char code (x4)
  w_linesp    !   user required line spacing
  w_savedo    !   fixed off / fixed line off before
  w_savedl    !   pixs from top / phy line of off
  w_savedc    !   change in phy line for shiftable
  w_savedh    !   w_height at multiop start
  w_savedlc   !   phy line of fixed for one op
  w_wrapwidth !   wordwrap width
  w_togminx   !   toggle size min x
  w_togminy   !   toggle size min y
  w_togmaxx   !   toggle size max x
  w_togmaxy   !   toggle size max y
  w_txtlen    !   size of cache line inc col masks
  w_palette   !   current palette data
  w_pwind     !   point (=this!) window offset
  w_pline     !   point row (y coord)
  w_pcol      !   point column (x coord)
  w_poff      !   point offset
  w_ploff     !   point line offset
  w_pwidth    !   point width
  w_saveds    !   start of area to redraw offset
  w_modedata  !   zap handled mode data block
  w_fontc     !   font cache offset / -1 if none
  w_font      !   font offset (of 1bpp form)
  w_savedscy  !   saved scrolly (s_txt_stat) ; (was RW 12)
  w_info      !   w_info (was RW13)
  w_res14     !   reserved word 14
  w_res15     !   reserved word 15
  w_res16     !   reserved word 16
  w_mode0     !   mode 0 word

 Name    Module
 Default r11
  m_zapworkspace   !   Zaps workspace pointer
  m_clearsel       !   0 if selection is clear
  m_scripttables   !   pointer to the first script table
End Workspace

Define Module
 Name    ZapJRF
 Author  Justin Fletcher
 Init    initialise
 Final   finalise
 SWIs
  Base   &90480
  Prefix ZapJRF
   0     RegisterScriptDirectives   swi_rsd
   1     DeRegisterScriptDirectives swi_dsd
 End SWIs
 Workspace  `len_Module
End Module

Pre
  LIBRARY "<Zap$Dir>.Docs.TechCode.E-Library"
  PROCdefine_zap_variables
  tab0=3:tab1=11:tab2=41
  car_cursor=5
  car_selection=7
  car_mode=10
  stkextra=1024*1:REM If you claim too much then Zap keeps claiming memory
End Pre

# REM OFF
; *******************************************************************
; Subroutine:   initialise
; Description:  Initialise the module
; Parameters:   as given in PRM
; Returns:      VS if error (r0->error block)
; *******************************************************************
.initialise
   STMFD   (sp)!,{r0-r5,r12,link}        ; Stack registers
   MOV     r11,r12                       ; r11=module workspace
   BL      findzap                       ; find the Zap module
   ADRVS   r0,$error                     ; if not found, then error
   BVS     $exit                         ; ... so jump out
   STRW    r12,m_zapworkspace            ; store workspace address
   ADR     r0,$comtable
 FNcall(Zap_AddCommands)
   MOV     r0,#0
   STRW    r0,m_clearsel                 ; selection currently clear
   STRW    r0,m_scripttables             ; no scripts registered
$exit
   XLDMFD  (sp)!,{r0-r5,r12,pc}          ; Return from call
$error
   EQUD    0
   EQUZA    "Zap not initialised"

$comtable
   EQUD    $comtable
   EQUD    zap_service                   ; Zap service call code
   EQUZA   "JRF_PATCHTAB"
   EQUD    com_patchtab
   EQUZA   "JRF_RUNFILE"
   EQUD    com_runfile
   EQUZA   "JRF_NOTIFY"
   EQUD    com_notify
   EQUZA   "JRF_ALTERSEL"
   EQUD    com_altersel
   EQUZA   "JRF_CLEARSEL"
   EQUD    com_clearsel
   EQUZA   "JRF_EACHLINE"
   EQUD    com_eachline
   EQUZA   "JRF_IF"
   EQUD    com_if
   EQUZA   "JRF_DROPMARK"
   EQUD    com_dropmark
   EQUZA   "JRF_SETFILETYPE"
   EQUD    com_setfiletype
   EQUZA   "RUNSCRIPT"
   EQUD    com_runscript
   EQUZA   "JRF_SCRIPTADDR"
   EQUD    com_scriptaddr
   EQUZA   "JRF_HELPWORD"
   EQUD    com_helpword
   EQUZA   "JRF_MENUFORMODE"
   EQUD    com_menuformode
   EQUD    0          ; end of table

; *******************************************************************
; Subroutine:   finalise
; Description:  Shutdown the module nicely
; Parameters:   as given in PRM
; Returns:      VS if error (r0->error block)
; *******************************************************************
.finalise
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   REM     "Finalising"
;    BL      findzap                       ; find zap
;    XBL     zap_service_ok,,0             ; zap service - shutting down
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call


; *******************************************************************
; Subroutine:   zap_service
; Description:  Service call handler
; Parameters:   r1 = reason code
;               r11 = undefined
;               r12 = zap workspace pointer
; Returns:      all preserved, except r0 may be corrupted
; *******************************************************************
.zap_service
;    STMFD   (sp)!,{r0-r5,link}            ; Stack registers
;    REM     "ZapService = %r1"
;    LDMFD   (sp)!,{r0-r5,link}            ; Return from call

   CMP     r1,#2                         ; has command table started up ?
;    CMPNE   r1,#0                         ; or are we shutting down ?
   MOVNE   pc,link                       ; return if not
.zap_service_ok                          ; temporarily
   STMFD   (sp)!,{r1-r5,r11-r12,link}    ; Stack registers
   BL      findworkspace                 ; find our workspace
   REM     "Read our workspace as %&b"
   CMP     r1,#0                         ; is it shutdown ?
   BEQ     $shuttingdown                 ; yep, so let's tell them all !
   MOV     r0,#0                         ; sub-reason 0 = starting
   LMOV    r1,#&90480                    ; Service_ZapJRFScripts
   XSWI    "OS_ServiceCall"              ; ask them if they want to start
   MOV     r12,r11                       ; r12-> our address
   ADR     r0,$`commandtable             ; our command table
   LADR    r1,0                          ; address of the top of the table
   BL      swi_rsd                       ; register 'our' table
   LDMFD   (sp)!,{r1-r5,r11-r12,pc}      ; Return from call
$shuttingdown
   MOV     r0,#1                         ; sub-reason 1 = dying
   LMOV    r1,#&90480                    ; Service_ZapJRFScripts
   XSWI    "OS_ServiceCall"              ; tell them
   LDRW    r1,m_scripttables             ; read the top of the table pointer
$loop
   CMP     r1,#0                         ; is that it ?
   STRWEQ  r1,m_scripttables             ; if so, store the null
   MOVEQ   r0,#0                         ; yes, we want to die !
   LDMEQFD (sp)!,{r1-r5,r11-r12,pc}      ; Return from call if so
   REM     "Attempting to release %&1"
   LDR     r2,[r1]                       ; read the 'next' block
   REM     "Next = %&2"
   XBL     release,r1                    ; release the current one
   MOV     r1,r2                         ; current = next
   B       $loop                         ; and around until they are all done

; our internal command table
; Must be in capitals otherwise things go screwy
$`commandtable
   EQUZA   "ELSE"
   EQUD    do_else
   EQUZA   "IFTEXT"
   EQUD    do_iftext
   EQUZA   "IFMODE"
   EQUD    do_ifmode
   EQUZA   "IFMODEN"
   EQUD    do_ifmoden
   EQUZA   "ENDIF"
   EQUD    do_endif
   EQUZA   "SCRIPTEND"
   EQUD    do_scriptend
   EQUZA   "STOP"
   EQUD    do_stop
   EQUD    0

; *******************************************************************
; Subroutine:   swi_rsd
; Description:  ZapJRF_RegisterScriptDirectives - adds a table to the
;               table of scripts currently known
; Parameters:   r0-> directive table
;               r1-> base address for offsets (usually start of module)
;               r2-> private word (passed as r3)
; Returns:      none
; *******************************************************************
.swi_rsd
   STMFD   (sp)!,{r0-r4,link}            ; Stack registers
   LDR     r3,[r12,#m_scripttables]      ; read script tables pointer
   MOV     r4,r0                         ; r2-> table
   MOV     r0,#16                        ; 16 bytes for 'next', 'table'
                                         ; 'base', 'private'
   BL      claim                         ; claim it
   STR     r3,[r0,#0]                    ; store the 'next' pointer
   STR     r4,[r0,#4]                    ; store the pointer to the table
   STR     r1,[r0,#8]                    ; store the base address
   STR     r2,[r0,#12]                   ; store the private word
   STR     r0,[r12,#m_scripttables]      ; store in our block
   LDMFD   (sp)!,{r0-r4,pc}              ; Return from call

; *******************************************************************
; Subroutine:   swi_dsd
; Description:  ZapJRF_DeRegisterScriptDirectives - removes a table
;               to the table of scripts currently known
; Parameters:   r0-> directive table
;               r1-> base address for offsets (usually start of module)
;               r2-> private word (passed as r3)
; Returns:      none
; *******************************************************************
.swi_dsd
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   MOV     r4,r0                         ; hang on to the table address
   ADD     r2,r12,#m_scripttables        ; the 'last' pointer
   LDR     r1,[r2]                       ; read the top of the table pointer
$loop
   CMP     r1,#0                         ; is that it ?
   LDMEQFD (sp)!,{r0-r5,pc}              ; Return from call if so
   LDR     r3,[r1,#4]                    ; read this one from the block
   LDR     r11,[r1]                      ; read the 'next' block
   CMP     r3,r4                         ; are they the same ?
   MOVNE   r2,r1                         ; if not, last=this
   STREQ   r11,[r2]                      ; if so, store next in last 'next'
   XBLEQ   release,r1                    ; if so, release the current one
   MOV     r1,r11                        ; current = next
   B       $loop                         ; and around until they are all done

; *******************************************************************
; Subroutine:   findzap
; Description:  Finds the Zap module so that it's workspace can be used
; Parameters:   none
; Returns:      r12->Zap's workspace
; *******************************************************************
.findzap
   STMFD   (sp)!,{r0-r5,link}
   MOV     r0,#18                    ; read module info
   ADR     r1,$zap_title
   SWI     "XOS_Module"              ; find zap
   MOV     r12,r4                    ; r12=zap workspace
   LDMFD   (sp)!,{r0-r5,pc}
$zap_title
   EQUS    "Zap"+CHR$0
   ALIGN

; *******************************************************************
; Subroutine:   findworkspace
; Description:  Return our workspace pointer in r11
; Parameters:   none
; Returns:      r11-> our workspace
; *******************************************************************
.findworkspace
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   LADR    r1,module_title               ; name of this module
   XSWI    "XOS_Module",18               ; find the module
   MOV     r11,r4                        ; copy to r11
                                         ; this was the bit that was causing
                                         ; zap to crash so frequently
   REM     "Workspace = %&B, %&4"
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call


; *******************************************************************
; Subroutine:   com_runfile
; Description:  QuickSave and Filer_Run the file. Wait for Shift
;               released.
; Parameters:   r8->window block
;               r9->file block
;               r10->cursor block
; Returns:      none
; *******************************************************************
   EQUD    0                             ; flags (none set)
.com_runfile
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
 FNcall(Zap_SaveFile)                    ; Save the file
   CMP     r0,#0                         ; Has it been saved safely ?
   BNE     $exit                         ; if not, we can't do owt...
$checkshift
   XSWI    "OS_Byte",121,&80             ; check shift key
   CMP     r1,#&FF                       ; is it pressed
   BEQ     $checkshift                   ; if so, wait
   LDRW    r0,f_name                     ; Get pointer to filename
   BL      strlen                        ; find it's length
   MOV     r5,r0                         ; r5=length
   ADD     r0,r1,#16                     ; add 16 (for prefix + terminator)
 FNcall(Zap_Claim)
   MOV     r1,r0                         ; r1=buffer
   ADR     r0,$command                   ; r0=source (command)
   BL      strcpy                        ; copy it
   ADD     r1,r1,#10                     ; move to end of string
   MOV     r0,r5                         ; string = filename
   BL      strcpy                        ; copy it
   SUB     r0,r1,#10                     ; r0=command line (moved back down)
   MOV     r4,r0                         ; store in case SWI corrupts
   SWI     "Wimp_StartTask"              ; run it
   MOV     r0,r4                         ; restore
 FNcall(Zap_Free)
$exit
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

$command
   EQUZA   "Filer_Run "

; *******************************************************************
; Subroutine:   com_clearsel
; Description:  Clear the current selection, recording that it is clear
; Parameters:   r10->cursor block
; Returns:      none
; *******************************************************************
   EQUD    0
.com_clearsel
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   BL      findworkspace                 ; read our workspace
   MOV     r0,#0                         ; 0 meaning selection is clear
   STRW    r0,m_clearsel                 ; store in module
 FNcall(Zap_ClearSel)
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

; *******************************************************************
; Subroutine:   com_altersel
; Description:  Alter the selection by the nearest end to cursor,
;               use old block if selection was not cleared by
;               com_clearsel
; Parameters:   r10-> cursor block
;               r8-> window block
;               r9-> file block
; Returns:      none
; *******************************************************************
   EQUD    0                             ; flags (none set)
.com_altersel
   STMFD   (sp)!,{r0-r7,link}            ; Stack registers
   BL      findworkspace                 ; read our workspace to r11
   BL      readselection                 ; where is the selection ?
                                         ; EQ set if no selection
   LDRW    r0,c_off                      ; get current cursor offset
   MOVEQ   r5,r0                         ; if none, start=cursor
   MOVEQ   r6,r0                         ; if none, end=cursor
   SUBS    r1,r0,r5                      ; r1=distance from start
   RSBMI   r1,r1,#0                      ;    made absolute
   SUBS    r2,r0,r6                      ; r1=distance from end
   RSBMI   r2,r2,#0                      ;    made absolute
   CMP     r1,r2                         ; is start dist<end dist
   MOVLT   r5,r0                         ; y = start=cursor
   MOVGE   r6,r0                         ; n = end=cursor
   MOV     r2,r5                         ; r2=start
   MOV     r3,r6                         ; r3=end
   REM     "%c26%c07%c04Selecting : %r2,%r3 - %&B"
 FNcall(Zap_Select)
   MOV     r0,#1                         ; 1 meaning non-clear selection
   STRW    r0,m_clearsel                 ; store in workspace
$notours
   LDMFD   (sp)!,{r0-r7,pc}              ; Return from call


; *******************************************************************
; Subroutine:   com_patchtab
; Description:  Insert spaces up to 3, 12, and 42 (inserting ; and
;               space after) for JFPatch assembler code.
; Parameters:   r8->window block
;               r9->file block
;               r10->cursor block
; Returns:      none
; *******************************************************************
   EQUD    0        ; flags (none set)
.com_patchtab
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   LDRW    r4,c_col                      ; r0=current column
   CMP     r4,#tab2
   BGT     $toofar
   MOVLE   r5,#tab2
   CMP     r4,#tab1
   MOVLT   r5,#tab1
   CMP     r4,#tab0
   MOVLT   r5,#tab0
 FNcall(Zap_StartOp)
$loop
   CMP     r4,r5                         ; are we there ?
   BEQ     $moved                        ; if so, don't add anything more
   MOV     r0,#&11                       ; command = insert (with buffering)
   LDRW    r1,c_off                      ; where to add data (current offset)
   MOV     r2,#1                         ; number of bytes
   ADR     r3,$data                      ; data to insert
 FNcall(Zap_Command)
   ADD     r4,r4,#1                      ; increment cur column
   B       $loop
$moved
   CMP     r4,#tab2                      ; is it 42 ? (ie comment ?)
   BNE     $earlyline                    ; if not, skip code
   MOV     r0,#&11                       ; insert
   LDRW    r1,c_off                      ; at current location
   MOV     r2,#2                         ; amount to insert
   ADR     r3,$comment                   ; get new data
 FNcall(Zap_Command)
$earlyline
 FNcall(Zap_StopOp)
$toofar
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call
$data
   EQUB    &20
$comment
   EQUS    "; "
   ALIGN

; *******************************************************************
; Subroutine:   com_notify
; Description:  Display a message in the minibuffer window, and remove
;               it when a key is pressed, passing the key on to Zap
; Parameters:   r0->string
;               r2=reason code, or key pressed + &8000
;               r8->window block
;               r9->file block
;               r10->cursor block
; Returns:      r0=key number if r2=key pressed
; *******************************************************************
   EQUD    (3<<3)+(1<<8)                 ; flags (string & ?)
.com_notify
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   TEQ     r2,#0                         ; is this a init call ?
   BEQ     $setupprompt
   STR     r2,[sp]                       ; insert character specified
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

$setupprompt
   BL      strlen                        ; find length of string
   ADD     r0,r1,#12                     ; add some more for regs
 FNcall(Zap_Claim)                       ;  so that we can claim memory
   STMIA   r0!,{r8,r9}                   ; store regs in block
   LDR     r2,[sp]                       ; get the string passed
   MOV     r1,r0                         ; r1=destination
   MOV     r0,r2                         ; r0=source
   BL      strcpy                        ; move the string
   SUB     r3,r1,#8                      ; move r3 for r11 for callback
                                         ; subtracting 8 for regs pointer
   MVN     r1,#NOT -1                    ; shortest delay possible
   ADR     r2,$callback                  ; routine to call
 FNcall(Zap_CallBack)
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

; we need to do this in a CallBack because otherwise it would appear
; that the window is cleared by subsequent commands
$callback
   STMFD   (sp)!,{r0-r9,link}            ; Stack registers
   STMFD   (sp)!,{r11}                   ; store r11 on stack for release
   LDMIA   r11!,{r8,r9}
 FNcall(Zap_MiniClear)
   ADR     r7,com_notify                 ; routine to return to
 FNcall(Zap_MiniStart)
   MOV     r0,r11
 FNcall(Zap_MiniPrompt)
 FNcall(Zap_MiniUpdate)
   LDMFD   (sp)!,{r0}                    ; r0-> claimed block
 FNcall(Zap_Free)                        ; release mem
   LDMFD   (sp)!,{r0-r9,pc}              ; Return from call


; *******************************************************************
; Subroutine:   noselection
; Description:  Display the warning No selection specified
; Parameters:   r8->window block
;               r9->file block
; Returns:      none
; *******************************************************************
.noselection
   STMFD   (sp)!,{r0-r1,link}            ; Stack registers
   ADR     r0,$message                   ; message
   MOV     r1,#(1<<31)                   ; default time delay and don't beep
 FNcall(Zap_Warning)                     ; display the warning
   LDMFD   (sp)!,{r0-r1,pc}^             ; Return from call
$message
   EQUZA   "No selection specified"

.bracket_exiterr
   MOV     r7,r0                         ; store error block in r7
   LDMFD   (sp)!,{r0}                    ; read pointer from stack
 FNcall(Zap_Free)                        ; release workspace
   STR     r7,[sp]                       ; store block on stack
   LDMFD   (sp)!,{r0-r7,link}            ; Restore registers
   ORRS    pc,link,#vbit                 ; return with v set

; *******************************************************************
; Subroutine:   bracketsection
; Description:  Places a string on either side of the section
; Parameters:   r3-> first string
;               r4-> second string
;               r5= offset for first string
;               r6= offset for second string
;               r8-> window block
;               r9-> file block
; Returns:      none
; *******************************************************************
.bracketsection
   STMFD   (sp)!,{r0-r6,link}            ; Stack registers
 FNcall(Zap_StartOp)                     ; start long operation (for undo)
   MOV     r0,r3                         ; r0-> first string
   BL      strlen                        ; find length
   MOV     r2,r1                         ; r2=length of file
   MOV     r1,r5                         ; r1=offset to add at (start)
   ADD     r6,r6,r2                      ; end+=length of first string
                                         ; r3 already = string to add
   MOV     r0,#1                         ; insert text command
 FNcall(Zap_Command)                     ; insert the text...

   MOV     r0,r4                         ; r0-> second string
   BL      strlen                        ; find length
   MOV     r2,r1                         ; r2=length of file
   MOV     r1,r6                         ; r1=offset to add at (end)
   MOV     r3,r4                         ; r3->string to add
   MOV     r0,#1                         ; insert text command
 FNcall(Zap_Command)                     ; insert the text...

 FNcall(Zap_StopOp)
   LDMFD   (sp)!,{r0-r6,pc}              ; Return from call


; *******************************************************************
; Subroutine:   getparams
; Description:  Read parameters from string
; Parameters:   r1-> string in claimed block of memory
; Returns:      r3-> first string
;               r4-> second string
;               r5-> third string (if any, or null if none)
; *******************************************************************
.getparams
   STMFD   (sp)!,{r0-r2,link}            ; Stack registers
   LDRB    r5,[r1],#1                    ; get break character
   CMP     r5,#0                         ; is it null ?
   BEQ     $notenough                    ; if so, then not enough parameters
   MOV     r0,r5
   MOV     r3,r1                         ; r3=start of first parameter
$loop1
   LDRB    r0,[r1],#1                    ; get byte
   CMP     r0,#0                         ; is it terminator ?
   BEQ     $notenough                    ; if so, then not enough parameters
   CMP     r0,r5                         ; is it the break char ?
   BNE     $loop1                        ; if not, loop again
   MOV     r0,#0
   STRB    r0,[r1,#-1]                   ; store over the break
   MOV     r4,r1
$loop2
   LDRB    r0,[r1],#1                    ; get byte
   CMP     r0,#0                         ; is it terminator ?
   CMPNE   r0,r5                         ; if not, is it the break char ?
   BNE     $loop2                        ; if not, loop again
   CMP     r0,#0                         ; was the break char a terminator ?
   MOVEQ   r5,#0                         ; if so, third string = 0
   MOVNE   r5,r1                         ; if not, third string = pointer
   MOV     r0,#0
   STRB    r0,[r1,#-1]                   ; store over the break
   LDMFD   (sp)!,{r0-r2,pc}              ; Return from call

$notenough
   REMP    "%c26%30ERROR IN GETPARAMS"
   LDMFD   (sp)!,{r0-r2,link}            ; Return from call
   ADR     r0,$error
   ORRS    pc,link,#vbit                 ; set V flag and return

$error
   EQUD    0
   EQUZA   "Not enough parameters specified (use <break><param1><break><param2>)"

; *******************************************************************
; Subroutine:   findword
; Description:  Return the location of the word under the caret
; Parameters:   r10->cursor block
;               r9->file block
; Returns:      r5->start of word
;               r6->end of word
; *******************************************************************
.findword
   STMFD   (sp)!,{r0-r4,link}            ; Stack registers
   LDRW    r4,f_len                      ; r4=length of file
   LDRW    r0,c_off                      ; get cursor offset
$loopback
   SUBS    r0,r0,#1                      ; move back one char
   BMI     $leftfound                    ; if SOF, exit search
   BL      readchar                      ; get a character
   CMP     r1,#32                        ; is it <=32 (ie ctrl or space) ?
   BGT     $loopback                     ; if not, continue search
                                         ; otherwise drop through
$leftfound
   ADD     r5,r0,#1                      ; r5=start of word (add over the
                                         ; character which broke)
   LDRW    r0,c_off                      ; get cursor offset
$loopforward
   ADD     r0,r0,#1                      ; move forward one char
   CMP     r0,r4                         ; are we at the end of the file ?
   BGE     $rightfound                   ; if EOF, exit search
   BL      readchar                      ; get a character
   CMP     r1,#32                        ; is it <=32 (ie ctrl or space) ?
   BLE     $rightfound                   ; if so, found right
                                         ; this bit is to ensure that
                                         ; words don't end with
                                         ; .,:; if they are followed by
                                         ; a space as in normal text
   CMP     r1,#ASC(".")                  ; is it . ?
   CMPNE   r1,#ASC(",")                  ; if not, how about , ?
   CMPNE   r1,#ASC(":")                  ;         or a colon ?
   CMPNE   r1,#ASC(";")                  ;         ; ?
   BNE     $loopforward                  ; if not, go forward one...
   ADD     r0,r0,#1                      ; move forward one char
   CMP     r0,r4                         ; are we at the end of the file ?
   SUBGE   r0,r0,#1                      ; if EOF, remove .,:;
   BGE     $rightfound                   ;         and exit search
   BL      readchar                      ; get a character
   CMP     r1,#32                        ; is it <=32 (ie ctrl or space) ?
   SUBLE   r0,r0,#1                      ; if so, then discount .,:;
   BGT     $loopforward                  ; if not, continue search
                                         ; otherwise, drop through
$rightfound
   MOV     r6,r0                         ; r6=end offset
   LDMFD   (sp)!,{r0-r4,pc}              ; Return from call

; *******************************************************************
; Subroutine:   readselection
; Description:  find out where the selection is
; Parameters:   r10-> cursor block
;               r11-> our workspace
; Returns:      r7-> car_selection
;               r5= start offset
;               r6= end offset
;               EQ if no selection
; *******************************************************************
.readselection
   STMFD   (sp)!,{r0-r1,link}            ; Stack registers
   MOV     r1,#car_selection             ; read the selection block pointer
 FNcall(Zap_ReadVar)                     ; get the value to r0
   MOV     r7,r0                         ; move to r7 (r7=car_selection)
   MOV     r1,#car_mode                  ; read the selction block pointer
 FNcall(Zap_ReadVar)                     ; get the value to r0
   CMP     r0,#1                         ; are we in std selection mode ?
   BNE     $notours                      ; if not, we can't process
   LDR     r5,[r7,#c_off]                ; start offset
   LDR     r6,[r7,#c_owidth]             ; end offset
   CMP     r5,r6                         ; is r5>r6 ? ie wrong way around
   SWAPGT  r5,r6                         ; if so, swap so that they are right
   LDR     r1,[r7,#c_wind]               ; get window number
   CMN     r1,#1                         ; is it -1 (ie no selection)
   LDRWEQ  r1,m_clearsel                 ; y= get the clear flag
   CMPEQ   r1,#0                         ; y= is it really no sel ?
   LDMFD   (sp)!,{r0-r1,pc}              ; Return from call
$notours
   CMP     r1,r1
   LDMFD   (sp)!,{r0-r1,pc}              ; Return from call with EQ set

; *******************************************************************
; Subroutine:   readchar
; Description:  Read a character from file buffer
; Parameters:   r0=file offset
;               r9->file block
; Returns:      r1=byte read
; *******************************************************************
.readchar
   STMFD   (sp)!,{r0,link}               ; Stack registers
   LDRW    r1,f_splito                   ; find split offset in buffer
   CMP     r0,r1                         ; are we in the bottom or top half?
   LDRWCS  r1,f_splits
   ADDCS   r0,r0,r1                      ; if in the top half, skip the split
   LDRW    r1,f_ptr                      ; start address of file
   LDRB    r1,[r1,r0]                    ; read the byte
   LDMFD   (sp)!,{r0,pc}                 ; Return from call

; *******************************************************************
; Subroutine:   com_eachline
; Description:  execute a group of Zap commands for each line in the
;               selection. Format of string passed is :
;               <break><command for start><break><command for middle>
;               <break><command for end>
; Parameters:   r0->string
;               r2=reason code
;               r8->window block
;               r9->file block
;               r10->cursor block
; Returns:      VS if error (r0->error block)
; Note:         This routine worked first time, bar one slight mistake!
; *******************************************************************
   EQUD    (3<<3)                        ; flags (string)
.com_eachline
   STMFD   (sp)!,{r0-r7,link}            ; Stack registers
; copy string
   BL      strlen                        ; find the length of the string
   ADD     r2,r1,#1                      ; add one for terminator
   MOV     r1,r0                         ; r1->original block
   MOV     r0,r2                         ; r0= length to claim
 FNcall(Zap_Claim)                       ; claim space for string (r0->space)
   SWAP    r0,r1                         ; swap source and dest for strcpy
   BL      strcpy                        ; move string to right place
                                         ; (r1->string)
   STMFD   (sp)!,{r1}                    ; store r1 on stack for release
   MOV     r7,r1                         ; r7-> parameters
; read selection
   MOV     r1,r7                         ; r0-> parameters
   BL      getparams                     ; r3,r4,r5->start, middle, end
   BVS     bracket_exiterr               ; if error, exit
                                         ; just happens to use same regs
   STMFD   (sp)!,{r3-r5}                 ; stack registers
   MOV     r7,r4                         ; r7-> middle string

 FNcall(Zap_GetSel)                      ; r1=start, r2=len
   ADDCS   sp,sp,#4*3                    ; if invalid, increment sp
   BCS     $nosel                        ;     and exit
 FNcall(Zap_StartOp)                     ; start a big group of operations
   ADD     r6,r1,r2                      ; r6=end of selection
   MOV     r0,r1                         ; r0=start of selection
$findstart
   SUBS    r0,r0,#1                      ; move back a character
   BMI     $startfound                   ; if -ve, we've found the start
   BL      readchar                      ; get a character from the file
   CMP     r1,#13                        ; is it CR ?
   CMPNE   r1,#10                        ; if not, how about LF ?
   BNE     $findstart                    ; if not, then keep searching
$startfound
   MOV     r4,r0                         ; r4=start of block
$nextcommand
   LDRW    r3,f_len                      ; r3=length of file
   ADD     r0,r0,#1                      ; add 1 to position, to make SOL
   STRW    r0,c_off                      ; store in caret block
   MOV     r5,r0                         ; r5=pointer
 FNcall(Zap_UpdateCaret)                 ; update the position vars
   MOV     r0,r7                         ; r0=command to execute
 FNcall(Zap_CommandString)               ; process the command
   CMN     r0,#1                         ; has a minibuffer been opened ?
   ADDEQ   sp,sp,#4*3                    ; if so, move sp up
   BEQ     $stopopandexit                ;    and stop processing
   LDRW    r0,f_len                      ; read file length
   SUBS    r0,r0,r3                      ; find difference made by command
   ADD     r6,r6,r0                      ; change end pointer appropriately
   ADD     r0,r5,r0                      ; add the change to current ptr
$findnext
   ADD     r0,r0,#1                      ; move cursor right
   CMP     r0,r6                         ; have we passed the end ?
   BGE     $stopop                       ; if so, stop processing
   BL      readchar                      ; read a character
   CMP     r1,#13                        ; is it CR ?
   CMPNE   r1,#10                        ; if not, is it a LF ?
   BNE     $findnext
   B       $nextcommand
; find next line start
$stopop
   BL      readchar                      ; get a character
   CMP     r1,#13                        ; is it CR ?
   CMPNE   r1,#10                        ; how about LF ?
   BEQ     $atlinestart                  ; if so, we've found it
   ADD     r0,r0,#1                      ; move right
   CMP     r0,r3                         ; compare to LOF
   BLE     $stopop                       ; if <, then keep going...
$atlinestart
   ADD     r6,r0,#1                      ; r6 (End) = pointer (+1 over CR)
   LDMFD   (sp)!,{r1,r2,r3}
   ADD     r4,r4,#1
   STRW    r4,c_off                      ; store start at c_off
   LDRW    r4,f_len                      ; r4=length of file
 FNcall(Zap_UpdateCaret)                 ; move the caret there
   MOV     r0,r1                         ; r1-> start commands
 FNcall(Zap_CommandString)               ; execute start command
   CMN     r0,#1                         ; did a minibuffer open ?
   BEQ     $stopopandexit                ; if so, stop
   LDRW    r0,f_len                      ; read file length
   SUBS    r0,r0,r4                      ; find difference made by command
   ADD     r6,r6,r0                      ; change end pointer appropriately
   STRW    r6,c_off                      ; store end at c_off
 FNcall(Zap_UpdateCaret)                 ; move the caret there
   MOV     r0,r3                         ; r0-> end commands
   CMP     r0,#0                         ; is this null ?
   BEQ     $stopopandexit                ; if so, stop
 FNcall(Zap_CommandString)               ; execute command
$stopopandexit
   MOV     r2,r4                         ; r2=start offset
   MOV     r3,r6                         ; r3=end offset
 FNcall(Zap_Select)                      ; select that region
 FNcall(Zap_StopOp)
$exit
   ADR     r0,$`updatewindow             ; the way to update the window
 FNcall(Zap_CommandString)               ; update the window contents
   LDMFD   (sp)!,{r0}                    ; read pointer from stack
 FNcall(Zap_Free)                        ; release workspace
   LDMFD  (sp)!,{r0-r7,pc}               ; Return from call

$nosel
   BL      noselection                   ; display warning
   B       $exit                         ; exit

$`updatewindow
   EQUZA   "TMT_UpdateWindow"

; *******************************************************************
; Subroutine:   com_if
; Description:  Simply execute the command passed in the string if the
;               string at the cursor matches that passed
; Parameters:   r0->string
;               r2=reason code
;               r8->window block
;               r9->file block
;               r10->cursor block
; Returns:      VS if error (r0->error block)
; *******************************************************************
   EQUD    (3<<3)                        ; flags (string)
.com_if
   STMFD   (sp)!,{r0-r7,link}            ; Stack registers
   BL      findworkspace                 ; read our workspace to r11
; Claim some stack space
   MOV     r0,#stkextra                  ; stkextra for stack space
 FNcall(Zap_Claim)                       ; claim it
   REM     "Claimed %&0"
   ADD     r1,r0,#stkextra               ; move to the end of the stack
   STMFD   r1!,{r0,sp}                   ; stack old sp and block pointer
   LDMFD   (sp),{r0}                     ; read the old r0
   MOV     sp,r1                         ; /this/ is the new stack pointer
; claim over
   BL      strlen                        ; find the length of the string
   ADD     r2,r1,#1                      ; add one for terminator
   MOV     r1,r0                         ; r1->original block
   MOV     r0,r2                         ; r0=length to claim
 FNcall(Zap_Claim)                       ; claim space for string (r0->space)
   SWAP    r0,r1                         ; swap source and dest for strcpy
   BL      strcpy                        ; move string to right place
                                         ; (r1->string)
   STMFD   (sp)!,{r1}                    ; store r1 on stack for release
   BL      getparams                     ; get the string parameters
   BVS     bracket_exiterr               ; exit if error
                                         ; r3-> compare string
                                         ; r4-> command to execute if true
                                         ; r5-> command to execute if not
   LDRW    r2,f_len                      ; r2=length of file
   LDRW    r0,c_off                      ; get cursor offset
$loop
   LDRB    r6,[r3],#1                    ; get byte from compare
   CMP     r6,#0                         ; end of compare ?
   BEQ     $match                        ; have found it
   CMP     r2,r0                         ; are we at EOF ?
   BLE     $nomatch                      ; if so, no match
   CMP     r6,#ASC("|")                  ; is it a ctrl char ?
   BNE     $notctrl                      ; if not, skip code
   LDRB    r6,[r3],#1                    ; get byte from compare
   CMP     r6,#0                         ; end of compare ?
   BEQ     $match                        ; have found it
   CMP     r2,r0                         ; are we at EOF ?
   BLE     $nomatch                      ; if so, no match
   CMP     r6,#ASC("|")                  ; is it itself a pipe ?
   SUBNE   r6,r6,#64                     ; and subtract the 64 for ctrl

$notctrl
   CMP     r6,#ASC("A")                  ;  (case insensitive)
   BLT     $notupperC
   CMP     r6,#ASC("Z")
   ADDLE   r6,r6,#32
$notupperC

   BL      readchar                      ; get byte from file

   CMP     r1,#ASC("A")                  ;  (case insensitive)
   BLT     $notupperR
   CMP     r1,#ASC("Z")
   ADDLE   r1,r1,#32
$notupperR

   CMP     r1,r6                         ; compare byte
   BNE     $nomatch                      ; if not same, exit
   ADD     r0,r0,#1                      ; increment pointer
   B       $loop                         ; otherwise, search again

; execute command
$match
   CMP     r4,#0                         ; is it undefined ?
   BEQ     $exit                         ; if so, exit
   LDRB    r0,[r4]                       ; get first byte
   CMP     r0,#32                        ; is it control ?
   BLT     $exit                         ; if so, exit
 FNcall(Zap_StartOp)                     ; start long operation
   MOV     r0,r4
 FNcall(Zap_CommandString)               ; execute the commands
 FNcall(Zap_StopOp)                      ; end the operation
   B       $exit                         ; and exit
$nomatch
   MOV     r4,r5                         ; do alternate string
   B       $match

$exit
   LDMFD   (sp)!,{r0}                    ; read pointer from stack
 FNcall(Zap_Free)                        ; release workspace
; unclaim stack
   LDMFD   (sp),{r0,sp}                  ; read start of area and sp (no WB)
   REM     "Freeing %&0"
 FNcall(Zap_Free)                        ; release space
; unclaim done
   LDMFD   (sp)!,{r0-r7,pc}              ; Return from call


; *******************************************************************
; Subroutine:   com_dropmark
; Description:  Drops a marker at the current cursor location
; Parameters:   r10-> cursor block
;               r8-> window block
;               r9-> file block
; Returns:      none
; *******************************************************************
   EQUD 0
.com_dropmark
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   LDRW    r0,c_off                      ; get cursor offset
 FNcall(Zap_InsertMark)                  ; none
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

; *******************************************************************
; Subroutine:   com_setfiletype
; Description:  Sets the current filetype of the file we are editing
; Parameters:   r10-> cursor block
;               r8-> window block
;               r9-> file block
; Returns:      none
; *******************************************************************
   EQUD    0+(2<<3)+(1<<13)              ; word value and only singular
.com_setfiletype
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   LDR     r1,[r0]                       ; read the parameter
   LDRW    r0,f_load                     ; get load address
   BIC     r0,r0,#&0FF00                 ; unset filetype bits
   BIC     r0,r0,#&F0000                 ; ... and the top part
   MOV     r1,r1,LSL #8                  ; shift filetype left
   ORR     r0,r0,r1                      ; set the filetype bits
   STRW    r0,f_load                     ; store back
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call


; *******************************************************************
; Subroutine:   com_scriptaddr
; Description:  Execute a script from memory
; Parameters:   r10-> cursor block
;               r9-> file block
;               r8-> window block
;               r0-> block of memory the script lives in
; Returns:      none
; *******************************************************************
   EQUD    (2<<3)+(1<<13)                ; words, in single entries
.com_scriptaddr
   STMFD   (sp)!,{r0-r8,link}            ; Stack registers
 FNcall(Zap_StartOp)                     ; start long operation (for undo)
   MOV     r5,#0                         ; r5 = no IF structures yet
   MOV     r11,#1                        ; 1 if we are executing, 0 otherwise
; open the file and things
   LDR     r7,[r0]                       ; read the address
   CMP     r7,#0                         ; is it 0 ?
   CMN     r7,#1                         ; or -1 ?
   BEQ     $invalid                      ; if so, leave nicely
   MOV     r0,r7                         ; start at address given
   ADD     r1,r7,#1                      ; end at the next byte
   SWI     "OS_ValidateAddress"          ; is it valid ?
   BCS     $invalid                      ; no, so leave nicely
; claim space for line and a 'temporary' buffer
   MOV     r0,#512                       ; space to claim
 FNcall(Zap_Claim)                       ; claim it
   MOV     r6,r0                         ; r6-> workspace
; claim space for 'if' stack
   MOV     r0,#256                       ; space to claim
 FNcall(Zap_Claim)                       ; claim it
   MOV     r4,r0                         ; r4-> ascending stack
$loop
   REM     "ScriptAddr: Reading line"
   MOV     r2,#0                         ; offset into string
; read the line into our buffer (makes it safe to modify if needed)
$readloop
   LDRB    r1,[r7,r2]                    ; get the first byte
   CMP     r1,#32                        ; is it a terminator ?
   MOVLT   r1,#0                         ; if so, make it 0
   STRB    r1,[r6,r2]                    ; store in 'our' block
   ADD     r2,r2,#1                      ; skip it character
   BGE     $readloop                     ; if not term, go around again

   MOV     r0,r7                         ; r0-> string (in our block)
   ADD     r7,r7,r2                      ; skip the block
   REM     "Readline %$0"

   BL      scriptline                    ; process a line of the script
   CMP     r11,#2                        ; is that /everything/ ?
   BNE     $loop                         ; and go around again

$done
   MOV     r0,r6                         ; r6-> string block to free
 FNcall(Zap_Free)                        ; release space
   MOV     r0,r4                         ; r4-> 'if' block to free
 FNcall(Zap_Free)                        ; release space

$invalid                                 ; for the time being return
 FNcall(Zap_StopOp)
   LDMFD   (sp)!,{r0-r8,pc}              ; Return from call

; *******************************************************************
; Subroutine:   com_runscript
; Description:  Execute a command file
; Parameters:   r10-> cursor block
;               r9-> file block
;               r8-> window block
;               r0-> string
; Returns:      none
; *******************************************************************
   EQUD    (3<<3)+(1<<14)                ; flags (string and no-trojan)
.com_runscript
   STMFD   (sp)!,{r0-r8,link}            ; Stack registers
 FNcall(Zap_StartOp)                     ; start long operation (for undo)
   MOV     r5,#0                         ; r5 = no IF structures yet
   MOV     r11,#1                        ; 1 if we are executing, 0 otherwise
; open the file and things
   MOV     r1,r0                         ; r1-> filename
   XSWI    "XOS_Find",&43                ; open the file
   BVS     $invalid                      ; if error it's invalid
   CMP     r0,#0                         ; is it valid ?
   BEQ     $invalid                      ; if not, jump out
   MOV     r7,r0                         ; r7 = file handle
; claim space for line and a 'temporary' buffer
   MOV     r0,#512                       ; space to claim
 FNcall(Zap_Claim)                       ; claim it
   MOV     r6,r0                         ; r6-> workspace
; claim space for 'if' stack
   MOV     r0,#256                       ; space to claim
 FNcall(Zap_Claim)                       ; claim it
   MOV     r4,r0                         ; r4-> ascending stack
$loop
   XBL     getlinefromfile,r6,r7,#256    ; read a line
   BCS     $done                         ; we're done !
   BL      scriptline                    ; process a line of the script
   CMP     r11,#2                        ; is that /everything/ ?
   BNE     $loop                         ; and go around again

$done
   XSWI    "XOS_Find",&0,r7              ; close the file
   MOV     r0,r6                         ; r6-> string block to free
 FNcall(Zap_Free)                        ; release space
   MOV     r0,r4                         ; r4-> 'if' block to free
 FNcall(Zap_Free)                        ; release space

$invalid                                 ; for the time being return
 FNcall(Zap_StopOp)
   LDMFD   (sp)!,{r0-r8,pc}              ; Return from call

; *******************************************************************
; Subroutine:   scriptline
; Description:  process a line from a script
; Parameters:   r0-> string
;               r4-> 'if' structure
;               r5 = if structure level
;               r6-> line buffer (r6+256 is private workspace)
;               <zap registers>
;               r11 = 1 if we are currently executing (if TRUE)
; Returns:      r4,r6,r7 preserved
;               r5,r11 updated (r11=2 to stop immediately)
;               <zap registers> preserved/updated
; *******************************************************************
.scriptline
   STMFD   (sp)!,{r4,r6,link}            ; Stack registers
$skipspaces
   LDRB    r1,[r0]                       ; get the first byte
   CMP     r1,#32                        ; is it a space ?
   CMPNE   r1,#9                         ; or a tab ?
   ADDEQ   r0,r0,#1                      ; if so, skip it
   BEQ     $skipspaces                   ; and go around again

   CMP     r1,#ASC("#")                  ; is it an 'internal' command ?
   BEQ     $internal                     ; if so, let's see what it is
   CMP     r1,#ASC("|")                  ; is it a comment ?
   CMPNE   r11,#0                        ; if not, are we 'false' ?
   BEQ     $exit                         ; if not, skip execution
 FNcall(Zap_StartOp)                     ; start long operation (for undo)
 FNcall(Zap_CommandString)               ; do it as a command
 FNcall(Zap_StopOp)                      ; stop long operation
   B       $exit                         ; and go around again

; process internal commands
$internal
   ADD     r0,r0,#1                      ; skip the '#'
   STMFD   (sp)!,{r0,r6}                 ; stack r0 and r6 temporarily
   STMFD   (sp)!,{r11-r12}               ; Stack registers
   BL      findworkspace                 ; get our workspace -> r11
   LDRW    r6,m_scripttables             ; address of our tables
   LDMFD   (sp)!,{r11-r12}               ; Unstack registers

$table_loop
   REM     "Checking table, address = %&6"
   CMP     r6,#0                         ; have we finished ?
   BEQ     $no_more_tables               ; would seem so
   LDR     r3,[r6,#4]                    ; read the table pointer
$int_next
   LDMFD   (sp),{r0}                     ; re-read r0 without wb
   LDRB    r1,[r3]                       ; read the first byte
   CMP     r1,#0                         ; is that it ?
   BEQ     $next_table                   ; if so, it's unrecognised - ignore
   REM     "%c04Checking string '%$0' against '%$3'"
$int_loop
   LDRB    r2,[r0],#1                    ; read a byte from their string +inc
   LDRB    r1,[r3],#1                    ; read a byte from our string +inc
   CMP     r2,#ASC("a")                  ; is it >= 'a'
   RSBGES  r14,r2,#ASC("z")              ; check for being <='z'
   SUBGE   r2,r2,#32                     ; if so, make upper case !
   CMP     r2,#9                         ; is it 'tab'
   MOVEQ   r2,#32                        ; if so, use 'space'
   CMP     r2,#32                        ; is it <32 ?
   MOVLT   r2,#0                         ; if so, make it 0
   CMPLE   r1,#0                         ; is either of these, eo our string?
   BEQ     $int_found                    ; if so, it's one of ours
   CMP     r1,r2                         ; are they the same ?
   BNE     $int_notfound                 ; if not, we want the next
   B       $int_loop                     ; check the next entry

$next_table
   LDR     r6,[r6]                       ; read the next pointer from table
   B       $table_loop

$no_more_tables
   LDMFD   (sp)!,{r0,r6}                 ; read stacked r0 and r6
   B       $exit                         ; and exit (not matched)

; get the next entry in the internal list
$int_notfound
   CMP     r1,#0                         ; is that the end of our string ?
   LDRNEB  r1,[r3],#1                    ; if not, read byte and increment
   BNE     $int_notfound                 ; if not, get next byte
   ADD     r3,r3,#7                      ; add 7 bytes (one word and align)
   BIC     r3,r3,#3                      ; word align it
   B       $int_next                     ; get the next command

; we've got the command
$int_found
   CMP     r1,#0                         ; is that the end of our string ?
   LDRNEB  r1,[r3],#1                    ; if not, read byte and increment
   BNE     $int_notfound                 ; if not, get next byte
$int_skipspaces
   LDRB    r14,[r0],#1                   ; read a byte and increment
   CMP     r14,#32                       ; is it space ?
   CMPNE   r14,#9                        ; or tab ?
   BEQ     $int_skipspaces               ; if so, skip 'em
   SUB     r0,r0,#1                      ; go back one
   CMP     r14,#32                       ; what did we read ?
   MOVLT   r2,#0                         ; if <32 then use 0
   ADD     sp,sp,#4                      ; skip the r0 we stacked
   ADD     r3,r3,#3                      ; add 3 bytes (align)
   BIC     r3,r3,#3                      ; word align it
   LDR     r1,[r3]                       ; read the offset of the routine
   LDR     r14,[r6,#8]                   ; get the address of the base
   LDR     r3,[r6,#12]                   ; read the private word
   LDR     r6,[sp],#4                    ; read the old r6
   ADD     r1,r1,r14                     ; make into a code offset
   ADR     r14,$exit                     ; we'll return to the end of this
; routines are called with :
;  r0-> first valid character
;  r1 (rubbish)
;  r2 = character we stopped on (32 for params or 0 if none)
;  r3 (rubbish)
;  r4-> 'if structure'
;  r5 = 'if structure' offset
;  r6-> start of line buffer (add 256 for a temporary buffer)
;  r7 = filehandle
;  r8-> window block
;  r9-> file block
;  r10-> cursor block
;  r11 = 1 if you should do your command
;  r12-> zap workspace
;  r14-> return address
; return with MOV pc,link
; on exit :
;  r7,r12 preserved
;  r8,r9,r10 preserved/updated
;  r5 preserved or incremented/decremented by 1
;  r11 preserved or toggled between 1 and 0, or set to 2 to stop
;  r0-r4,r6 corrupt
   MOV     pc,r1                         ; call it

$exit
   LDMFD   (sp)!,{r4,r6,pc}              ; Return from call

.do_scriptend
   MOV     r11,#2                        ; stop immediately
   MOV     pc,link                       ; and return

.do_stop
   TEQ     r11,#1                        ; are we executing ?
   MOVEQ   r11,#2                        ; if so, stop
   MOV     pc,link                       ; return from call

.do_else
   STMFD   (sp)!,{r0-r4,link}            ; Stack registers
   REM     "Else, old state %rB, level %r5"
   CMP     r5,#0                         ; are we at the top ?
   EOREQ   r11,r11,#1                    ; if so, just invert the status
   BEQ     $exit                         ;        and exit
   SUB     r0,r5,#1                      ; go back to the prior level
   LDRB    r1,[r4,r0]                    ; read it's value
   REM     "State of level above was %r1"
   CMP     r1,#1                         ; was it 'true' ?
   EOREQ   r11,r11,#1                    ; if so, invert the current state
$exit
   REM     "%c04ELSE, new state is %rB"
   LDMFD   (sp)!,{r0-r4,pc}              ; Return from call

.do_iftext
   STMFD   (sp)!,{r4,link}               ; Stack registers
   REM     "%c04IFTEXT still being tested"
   CMP     r2,#0                         ; was there a parameter passed ?
   BEQ     $isfalse                      ; if not, mark it as FALSE
   LDRB    r2,[r0],#1                    ; read the first byte (+inc)
   REM     "First byte is %a2 (%r2)"
   CMP     r2,#ASC("""")                 ; was it a " ?
   BNE     $isfalse                      ; if not, mark it as FALSE
   REM     "Is a valid command"
   MOV     r3,r0                         ; r3-> compare string
   LDRW    r2,f_len                      ; r2=length of file

   MOV     r1,#car_cursor                ; read the cursor block pointer
 FNcall(Zap_ReadVar)                     ; get the value to r0
   MOV     r10,r0                        ; r10 = cursor offset
   LDRW    r0,c_off                      ; get cursor offset
   LDRW    r1,c_loff                    ; get start of line offset
   REM     "Start of line at %r1, our location %r0"
   CMP     r0,r1                         ; are we at the start of the line ?
   LDREQ   r14,[r8,#w_format]            ; y-read the mode we are in
   ANDEQ   r14,r14,#255                  ; y-leave just the mode itself
   CMPEQ   r14,#5                        ; y-are we in BASIC mode ?
   ADDEQ   r0,r0,#3                      ; skip the line number ;-(
 FNcall(Zap_JumptoOffset)                ; make it align in Basic mode ?
   LDRW    r0,c_off                      ; get cursor offset (again!)
$loop
   LDRB    r4,[r3],#1                    ; get byte from compare
   CMP     r4,#32                        ; is it ctrl ?
   BLT     $isfalse                      ; if so, it's invalid and so FALSE
   REM     "Read compare character %a4 (%r4) loc=%r0,end=%r2"
   CMP     r4,#ASC("""")                 ; end of compare ?
   BEQ     $istrue                       ; we have found it
   CMP     r2,r0                         ; are we at EOF ?
   BLE     $isfalse                      ; if so, no match
   CMP     r4,#ASC("|")                  ; is it a ctrl char ?
   BNE     $notctrl                      ; if not, skip code
   LDRB    r4,[r3],#1                    ; get byte from compare
   CMP     r4,#32                        ; is it ctrl ?
   BLT     $isfalse                      ; if so, it's invalid
   CMP     r4,#ASC("|")                  ; is it itself a pipe ?
   CMPNE   r4,#ASC("""")                 ; or a " symbol ?
   SUBNE   r4,r4,#64                     ; and subtract the 64 for ctrl

$notctrl
   CMP     r4,#ASC("a")                  ; is it >= 'a'
   RSBGES  r14,r4,#ASC("z")              ; check for being <='z'
   SUBGE   r4,r4,#32                     ; if so, make upper case !

   REM     "Converted compare to %a4 (%r4)"
   BL      readchar                      ; get byte from file
   REM     "Read character %a1 from text (%r1)"

   CMP     r1,#ASC("a")                  ; is it >= 'a'
   RSBGES  r14,r1,#ASC("z")              ; check for being <='z'
   SUBGE   r1,r1,#32                     ; if so, make upper case !
   REM     "Converted text to %a1 (%r1)"

   CMP     r1,r4                         ; compare byte
   BNE     $isfalse                      ; if not same, exit
   ADD     r0,r0,#1                      ; increment pointer
   B       $loop                         ; otherwise, search again

$istrue
   REM     "Was TRUE"
   LDR     r4,[sp]                       ; re-read r4 from stack
   BL      do_iftrue                     ; mark us as being TRUE
   B       $exit                         ; and jump out
$isfalse
   REM     "Was FALSE"
   LDR     r4,[sp]                       ; re-read r4 from stack
   BL      do_iffalse                    ; mark us as being FALSE
$exit
   LDMFD   (sp)!,{r4,pc}                 ; Return from call

.do_endif
   STMFD   (sp)!,{r4,link}               ; Stack registers
   CMP     r5,#0                         ; are we at the top ?
   SUBNE   r5,r5,#1                      ; if not, move up the stack
   LDRNEB r11,[r4,r5]                   ;         read the old value
   MOVEQ   r11,#1                        ; if so, set current state to TRUE
   REM     "%c04ENDIF, new state is %rB"
   LDMFD   (sp)!,{r4,pc}                 ; Return from call

.do_ifmode
   STMFD   (sp)!,{r4,link}               ; Stack registers
   LDR     r3,[r8,#w_format]             ; read the mode we are in
   AND     r3,r3,#255                    ; leave just the mode itself
   REM     "%c04IFMODE entered with r2=%r2, in mode %r3"
   CMP     r2,#0                         ; were parameters passed ?
   BEQ     $isfalse                      ; if not, we'll take it as FALSE
   REM     "Params were '%$0'"
   MOV     r1,r0                         ; the string to convert
$num_loop
   XSWI    "XOS_ReadUnsigned",10         ; convert the number
   BVS     $isfalse                      ; if there's an error, it's FALSE
   REM     "Number was %r2"
   CMP     r2,r3                         ; are they the same ?
   BLEQ    do_iftrue                     ; if so, mark as a TRUE state
   BEQ     $exit                         ; and exit
   LDRB    r2,[r1],#1                    ; read the terminator and inc
   REM     "Terminator was %a2 (%r2)"
   CMP     r2,#ASC(",")                  ; was it a , ?
   BEQ     $num_loop                     ; if so, go around again
$isfalse
   BL      do_iffalse
$exit
   LDMFD   (sp)!,{r4,pc}                 ; Return from call

.do_ifmoden
   STMFD   (sp)!,{r4,r6-r8,link}         ; Stack registers
   LDR     r3,[r8,#w_format]             ; read the mode we are in
   AND     r3,r3,#255                    ; leave just the mode itself
   REM     "%c04IFMODEN entered with r2=%r2, in mode %r3"
   CMP     r2,#0                         ; were parameters passed ?
   BLEQ    do_iffalse                    ; it's false so mark it
   BEQ     $exit                         ; and go out
   REM     "Params were '%$0'"
   MOV     r6,r0                         ; hang on to the string we're using
   MOV     r0,r3                         ; the current mode
 FNcall(Zap_ReadMode)                    ; get the mode we're in
   LDR     r8,[r1,#e_title*2]            ; read the current mode name
   REM     "Looking for %$8"
   MOV     r0,r6                         ; the string to convert
$number_loop
   MOV     r7,#0                         ; start at the start of the string
$name_loop
   LDRB    r2,[r0,r7]                    ; read a char from their string
   REM     "Theirs %a2"
   CMP     r2,#ASC("a")                  ; is it >= 'a'
   RSBGES  r14,r2,#ASC("z")              ; check for being <='z'
   SUBGE   r2,r2,#32                     ; if so, make upper case !
   LDRB    r1,[r8,r7]                    ; read a char from our string
   REM     "Ours %a1"
   CMP     r1,#ASC("a")                  ; is it >= 'a'
   RSBGES  r14,r1,#ASC("z")              ; check for being <='z'
   SUBGE   r1,r1,#32                     ; if so, make upper case !

   CMP     r2,#32                        ; is it space (or lower) ?
   MOVLTS  r2,#0                         ; if so, it's the end of the string
   CMPNE   r2,#ASC(",")                  ; is that their string done ?
   BNE     $notterminator                ; not a terminator
   CMP     r1,#32                        ; is ours ended too ?
   BLGT    do_iffalse                    ; nope, so they don't
   BLLE    do_iftrue                     ; they are, so they match
   B       $exit                         ; so let's go out
$notterminator
   CMP     r2,r1                         ; are they the same ?
   ADDEQ   r7,r7,#1                      ; if so, next character
   BEQ     $name_loop                    ;        and go around again
; don't match, so skip to end of string
$findnext
   CMP     r2,#32                        ; is that it ?
   BLLE    do_iffalse                    ; if so, it's FALSE
   BLE     $exit                         ;        and go out
   CMP     r2,#ASC(",")                  ; is it , ?
   ADD     r7,r7,#1                      ; next one
   ADDEQ   r0,r0,r7                      ; if so, that's our offset
   BEQ     $number_loop                  ; check the next one
   LDRB    r2,[r0,r7]                    ; read a char from our string
   B       $findnext                     ; try the next character

$exit
   REM     "Current status %rB"
   LDMFD   (sp)!,{r4,r6-r8,pc}           ; Return from call


; *******************************************************************
; Subroutine:   do_iftrue
; Description:  Stack a 'TRUE' state on the stack
; Parameters:   r11= current state
;               r4-> 'if' structure
;               r5 = 'if' structure offset
; Returns:      r5 incremented
;               r11 preserved !
; Note:         This CANNOT be a command (would be pointless anyhow)
; *******************************************************************
.do_iftrue
   REM     "Is TRUE, level was %r5, state %rB"
   STRB    r11,[r4,r5]                   ; store the current 'if' state
   ADD     r5,r5,#1                      ; inc the offset
                                         ; if it /was/ true, we stay true
                                         ; and vice-versa
   MOVS    pc,link                       ; return, preserving flags

; *******************************************************************
; Subroutine:   do_iffalse
; Description:  Stack a 'FALSE' state on the stack
; Parameters:   r11= current state
;               r4-> 'if' structure
;               r5 = 'if' structure offset
; Returns:      r5 incremented
;               r11 = 0
; Note:         This CANNOT be a command (would be pointless anyhow)
; *******************************************************************
.do_iffalse
   REM     "Is FALSE, level was %r5, state %rB"
   STRB    r11,[r4,r5]                   ; store the current 'if' state
   ADD     r5,r5,#1                      ; inc the offset
   MOV     r11,#0                        ; we're in a 'false' block
   MOVS    pc,link                       ; return, preserving flags

; #Rem on
; *******************************************************************
; Subroutine:   com_helpword
; Description:  Try to launch some help on the word at the cursor
; Parameters:   r10-> cursor block
;               r9-> file block
;               r8-> window block
; Returns:      none
; *******************************************************************
   EQUD    0<<3
.com_helpword
   STMFD   (sp)!,{r0-r6,link}            ; Stack registers
   REM     "%c04%c30HelpWord called"
   LDRBW   r0,w_format                    ; read the mode number
   REM     "Mode for this window = %r0"
 FNcall(Zap_ReadMode)
   CMP     r0,#0
   BLT     $exit                         ; if <0 invalid
   MOV     r6,r0                         ; r6-> unlinked block (for later)
   LMOV    r2,#e_title*2
   LDR     r0,[r1,r2]                    ; read title offset
   REM     "Mode name = %$0"
   LADR    r1,$`modename                 ; the destination
   BL      strcpy                        ; copy it there
   LADR    r0,$`varname                  ; address of name
   REM     "Variable name = %$0"
   LADR    r1,`space                     ; r1-> space to copy to
   XSWI    "XOS_ReadVarVal",,,128,0,0
   BVS     $nomodepath                   ; if error, it's not there

   MOV     r0,#0
   STRB    r0,[r1,r2]
   REM     "Variable value = %$1"

   MOV     r0,#32                        ; space to store
   STRB    r0,[r1,r2]                    ; store it there
   ADD     r1,r1,#1                      ; add one to pointer (skip term)
   ADD     r1,r1,r2                      ; r1-> terminator
   RSB     r2,r2,#127                    ; r2 = 127 - r2
; at this point r2 = space left in buffer
;               r1-> position in buffer
$nomodepath
   MOV     r5,r2                         ; r5 = space left
   MOV     r4,r1                         ; r4-> place to store in buffer
; at this point r5 = space left in buffer
;               r4-> position in buffer
   REM     "Checking they support the e_help entry"
   LDR     r0,[r6,#e_len]                ; read the length
   REM     "Length of entries = %r0"
   CMP     r0,#e_help                    ; is it long enough for e_help ?
   BLE     $noe_help

; always do it for the time being
   B       $noe_help


   REM     "Getting help"
   LDRBW   r0,w_flags                    ; read the mode number
   MOV     r11,#e_help                   ; call the help entry
 FNcall(Zap_CallMode)
   CMP     r0,#0                         ; is r0 = 0 ?
   BEQ     $exit                         ; if so, vanish !
   REM     "%r0 help words provided"
   STMFD   (sp)!,{r0-r1}                 ; stack the values for the moment
   LDR     r0,[r1]                       ; read the first value from list
   MOV     r1,r4                         ; r1-> destination (end of string)
   BL      strcpy                        ; copy it
   BL      callhelpforword
   LDMFD   (sp)!,{r4-r5}                 ; unstack the values
; r4 = number of entries
; r5-> heap block of heap pointers
   MOV     r3,r5                         ; hang on to the block pointer
$loop
   LDR     r0,[r5],#4                    ; read from block and inc
 FNcall(Zap_Free)
   SUBS    r4,r4,#1                      ; decrement the counter
   BNE     $loop                         ; if <>0 go release more
   MOV     r0,r3                         ; r0-> heap block itself
 FNcall(Zap_Free)                        ; and release it...
$exit
   LDMFD   (sp)!,{r0-r6,pc}              ; Return from call

; there's no e_help entry so we're going to guess - fun !
; at this point r5 = space left in buffer
;               r4-> position in buffer
$noe_help
   REM     "No e_help was available, I'm guessing now !"
   LDRW    r3,f_len                      ; r4=length of file
   LDRW    r0,c_off                      ; get cursor offset
   TEQ     r0,#0                         ; is that the start
   BEQ     $startfound                   ; if so, we're done
$searchforstart
   REM     "Reading char at %&0"
   BL      readchar                      ; get a character
   BL      $checkvalidid                 ; check it's valid
   BNE     $foundinvalid                 ; if not, skip the looping stuff

   REM     "Stepping back..."
   SUBS    r0,r0,#1                      ; decrement counter
   BPL     $searchforstart               ; if not 0, go for more
$foundinvalid
   ADD     r0,r0,#1                      ; skip the invalid character
$startfound
   REM     "Found start of line, now for the end"

; this deals with BASIC, so be careful !
   LDRBW   r1,w_format                   ; read the mode number
   REM     "Mode is %r1"
   TEQ     r1,#5                         ; is it BASIC ?
   BNE     $notbasic
   TEQ     r0,#0                         ; is it 0 ?
   BEQ     $exit                         ; if so, it can't be ours
   MOV     r2,r0                         ; hang on to PROC string

   REM     "Basic processing begins"
   SUBS    r0,r0,#1                      ; go back one
   BEQ     $exit                         ; can't be ours
   MOV     r6,#0                         ; r6 = 0 (not a class type item)
$basicloop
   BL      readchar                      ; get a char
   REM     "Char fetched = %r1"
   CMP     r1,#ASC("")                  ; is it FN ?
   ADREQ   r0,$`fn                       ; if so, copy FN to it...
   BEQ     $bfoundstring                 ; we've got it...
   CMP     r1,#ASC("")                  ; is it PROC ?
   ADREQ   r0,$`proc                     ; if so, copy PROC to it...
   BEQ     $bfoundstring                 ; we've got it...
   CMP     r1,#ASC(".")                  ; is it a . ?
   BNE     $basicsimple                  ; if not, it's a simple string

   MOV     r6,#1                         ; a class type item
$bfindtype
   REM     "Basic - Stepping back..."
   SUBS    r0,r0,#1                      ; go back one
   BEQ     $exit                         ; can't be ours
   REM     "Basic - Reading char at %&0"
   BL      readchar                      ; get a character
   BL      $checkvalidid                 ; check it's valid
   TEQNE   r1,#ASC("@")                  ; is it an @ ?
   BNE     $basicloop                    ; if not, check it's type
   B       $bfindtype                    ; if not 0, go for more

$`fn
   EQUZ    "FN"
$`proc
   EQUZ    "PROC"
$`inst
   EQUZ    "(inst)"
   ALIGN
$bfoundstring
   REM     "Copying string %$0"
   LDRB    r14,[r0],#1                   ; read byte and inc
   TEQ     r14,#0                        ; was it 0 ?
   BEQ     $basiccheckclass              ; if so, we're checking class
   STRB    r14,[r4],#1                   ; store in buffer and inc
   SUBS    r5,r5,#1                      ; decrement space left
   BEQ     $exit                         ; not enough space
   B       $bfoundstring                 ; jump around again

$basiccheckclass
   TEQ     r6,#1                         ; is it a class item ?
   BNE     $basicsimple                  ; nope so we're done
   ADR     r0,$`inst                     ; r0-> instance name
$bcopyinst
   LDRB    r14,[r0],#1                   ; read byte and inc
   TEQ     r14,#0                        ; was it 0 ?
   BEQ     $basicsimple                  ; if so, we're done
   STRB    r14,[r4],#1                   ; store in buffer and inc
   SUBS    r5,r5,#1                      ; decrement space left
   BEQ     $exit                         ; not enough space
   B       $bcopyinst                    ; jump around again

$basicsimple
   MOV     r0,r2                         ; r0 = offset of text
$notbasic
; end of basic specific stuff
   BL      readchar                      ; read a character
   CMP     r1,#32                        ; is it ctrl ?
   BGT     $notcontrol                   ; if not, skip this bit

   SUBS    r5,r5,#4                      ; check enough space
   BLE     $exit                         ; not enough space

   MOV     r2,#ASC("!")                  ; !x## we use
   STRB    r2,[r4],#1                    ; store and inc
   MOV     r2,#ASC("x")                  ; !x## we use
   STRB    r2,[r4],#1                    ; store and inc
   MOV     r2,r1,LSR #4                  ; divide by 16
   CMP     r2,#10                        ; is it >10 ?
   ADDGE   r2,r2,#ASC("A")-10            ; if so, make it upper hex
   ADDLT   r2,r2,#ASC("0")               ; if not, make it a number
   STRB    r2,[r4],#1                    ; store and inc
   AND     r2,r1,#15                     ; leave just low nibble
   CMP     r2,#10                        ; is it >10 ?
   ADDGE   r2,r2,#ASC("A")-10            ; if so, make it upper hex
   ADDLT   r2,r2,#ASC("0")               ; if not, make it a number
   STRB    r2,[r4],#1                    ; store and inc
   B       $cfoundinvalid

$notcontrol
$copystring
   BL      readchar                      ; get a character
   BL      $checkvalidid                 ; check it's valid
   BNE     $cfoundinvalid                ; if not, skip the looping stuff

   REM     "Got char %a1"
   STRB    r1,[r4],#1                    ; store and inc
   SUBS    r5,r5,#1                      ; decrement space left
   BEQ     $exit                         ; not enough space
   ADD     r0,r0,#1                      ; decrement counter
   CMP     r0,r3                         ; compare to length
   BLT     $copystring                   ; if not 0, go for more
$cfoundinvalid
   MOV     r0,#0                         ; terminator
   STRB    r0,[r4]                       ; terminate it
   BL      callhelpforword               ; call it
   B       $exit                         ; exit, 'cos I don't know what to do

$`varname
   EQUS    "Zap$HelpPath$Mode_"
$`modename
   RES     20
   ALIGN

; check that r1 is valid in an id
$checkvalidid
   TEQ     r1,#ASC("_")                  ; is it an _ ?
   BEQ     $validid                      ; if so, then it's an id
; numbers
   CMP     r1,#ASC("0")                  ; is it a number ?
   BLT     $notvalidid                   ; if <, it's not valid
   CMP     r1,#ASC("9")                  ; is it a number ?
   BLE     $validid                      ; if <, it's a number (valid)
; upper case
   CMP     r1,#ASC("A")                  ; is it a upper ?
   BLT     $notvalidid                   ; if <, it's not valid
   CMP     r1,#ASC("Z")                  ; is it a upper ?
   BLE     $validid                      ; if <, it's a number (valid)
; lower case
   CMP     r1,#ASC("a")                  ; is it a lower ?
   BLT     $notvalidid                   ; if <, it's not valid
   CMP     r1,#ASC("z")                  ; is it a lower ?
   BLE     $validid                      ; if <, it's a number (valid)
$notvalidid
   CMP     pc,#0                         ; return 'NE'
   MOV     pc,link                       ; return
$validid
   CMP     r0,r0                         ; return 'EQ'
   MOV     pc,link                       ; return

; *******************************************************************
; Subroutine:   callhelpforword
; Description:  actually do the help for the word
; Parameters:   block at `space set up correctly
; Returns:      none
; *******************************************************************
>callhelpforword
   STMFD   (sp)!,{r0-r6,link}            ; Stack registers

   ADR     r0,`space                     ; pointer to string
   REM     "Help called for = %$0"

   LMOV    r0,#&43b00+(1<<28)+(%11<<30)  ; recorded and watch for bounce
   ADR     r1,$`wimpmsg                  ; pointer to block
   MOV     r2,#0                         ; broadcast
   LMOV    r3,#(1<<31)+20                ; offset of string to terminate with
   MOV     r4,#0                         ; no icon
   ADR     r5,help_msgbounce             ; code to call when bounced
   MOV     r6,#0                         ; no private
 FNcall(Zap_SendMessage)                 ; send the message
   LDMFD   (sp)!,{r0-r6,pc}              ; Return from call

$`wimpmsg
   EQUD    0                             ; length
   EQUD    0                             ; task
   EQUD    0                             ; theirref
   EQUD    0                             ; ourref
   EQUD    0                             ; message num
$`message
   EQUS    "Help_Word "
.`space
   RES     128
   ALIGN

; *******************************************************************
; Subroutine:   help_msgbounce
; Description:  deal with the message bouncing
; Parameters:   r0 = 19 (if bounced)
;               r1-> message block
;               r2 = message number
; Returns:      none
; *******************************************************************
>help_msgbounce
   STMFD   (sp)!,{r0-r6,link}            ; Stack registers
   BL      $launch
   LMOV    r0,#&43b00+(0<<28)+(%00<<30)  ; just send it
   MOV     r2,#0                         ; broadcast
   LMOV    r3,#(1<<31)+20                ; offset of string to terminate with
   MOV     r4,#0                         ; no icon
   MOV     r5,#0                         ; no routine to call
   MOV     r6,#0                         ; no private
 FNcall(Zap_SendMessage)                 ; send the message
   LDMFD   (sp)!,{r0-r6,pc}              ; Return from call

$launch
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   XSWI    "XOS_ReadVarVal",^$`varname,0,-1,0,0
   CMP     r2,#0                         ; is r2 -ve ?
   BGE     $notfound                     ; if not, it doesn't exist
   XSWI    "XWimp_StartTask",^$`command  ; run program
$notfound
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call
$`varname
   EQUZ    "StrongHelp$Dir"
$`command
   EQUZA   "/<StrongHelp$Dir>"

; #Rem On
; *******************************************************************
; Subroutine:   com_menuformode
; Description:  Creates a menu from a file for a particular mode
; Parameters:   r10-> cursor block
;               r9-> file block
;               r8-> window block
; Returns:      none
; Note:         Syntax is going to be :
;               JRF_MENUFORMODE "Basic=<file> Text=<file> *=<file>
; *******************************************************************
   EQUD    (7<<0)+(3<<3)+(1<<15)+(1<<18)+(1<<19)
.com_menuformode
   STMFD   (sp)!,{r0-r5,link}            ; Stack registers
   TEQ     r2,#15                        ; are we ticked/shaded ?
   BEQ     $parsefortick
   TEQ     r2,#18                        ; what should our text be ?
   BEQ     $getmenutext
   TEQ     r2,#19                        ; is this a submenu request ?
   BEQ     $submenurequired
$exit
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

; we need to generate the submenu
$submenurequired
   REM     "%c04%c30Submenu required for %$0"
   TEQ     r8,#0                         ; is r8 = 0 ? (no window)
   BEQ     $smr_notvalid                 ; if so, we can do nothing

   MOV     r5,r0                         ; r5-> string
   LDR     r2,$`lastptr                  ; read last menu block
   TEQ     r2,#0                         ; was it 0 ?
   BEQ     $smr_dontrelease              ; If so, we don't release it

; we DONT have to release the individual blocks it seems
;   MOV     r4,r2                         ; r4-> block
;   LDR     r3,[r4],#4                    ; read number of entries and inc
;$smr_releasenext
;   SUBS    r3,r3,#1                      ; decrement counter
;   BLE     $smr_allreleased
;   LDR     r0,[r4],#4                    ; read handle of block
;   TEQ     r0,#0                         ; is it valid ?
;   BNE     $smr_releasenext
; FNcall(Zap_Free)                        ; release
;   B       $smr_releasenext
;

$smr_allreleased
; now the main block
;    MOV     r0,r2
;  FNcall(Zap_Free)
;    MOV     r0,#0
;    STR     r0,$`lastptr                  ; ensure we don't release again
$smr_dontrelease

   LDRB    r0,[r8,#w_format]             ; read the mode we are in
 FNcall(Zap_ReadMode)                    ; get the mode we're in
   LDR     r2,[r1,#e_title*2]            ; read the current mode name

   BL      $findmodevalidity
   BNE     $smr_notvalid
   ADD     r0,r5,#1                      ; r0-> filename
   REM     "Loading file %$0"
 FNcall(Zap_LoadMenu)
   TEQ     r0,#0                         ; did it fail ?
   BEQ     $smr_notvalid                 ; yep, so we failed
   REM     "Loaded, handle = %&0"
   STR     r0,$`lastptr                  ; store the handle for later
   LDR     r0,[r0,#4]                    ; r0-> menu 0
   B       $smr_valid                    ; it's valid !!!

$smr_notvalid
   MOV     r0,#0                         ; no menu yet
$smr_valid
   STR     r0,$`lastptr                  ; store the pointer
   STR     r0,[sp]                       ; store as return r0
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

$`lastptr
   EQUD    0

; get our text string to use
$getmenutext
   REM     "%c04%c30Get menu text for %$0"
   TEQ     r8,#0                         ; is the window null ?
   ADREQ   r0,$`gmt_none                 ; there is no mode specified
   BEQ     $gmt_copyname
   MOV     r5,r0                         ; r5-> string
   LDRB    r0,[r8,#w_format]             ; read the mode we are in
 FNcall(Zap_ReadMode)                    ; get the mode we're in
   LDR     r0,[r1,#e_title*2]            ; read the current mode name
$gmt_copyname
   ADR     r1,$`modenamebuf              ; where to store data
   STR     r1,[sp]                       ; store as r1 return
   MOV     r3,#0                         ; count of characters
$gmt_loop
   TEQ     r3,#31                        ; are we done ?
   BEQ     $gmt_fill
   LDRB    r2,[r0],#1                    ; read byte and inc
   CMP     r2,#32                        ; is it ctrl ?
   STRGEB  r2,[r1,r3]                    ; if not, store at position
   ADDGE   r3,r3,#1                      ; inc counter
   BGE     $gmt_loop                     ; if not, go around more

$gmt_fill
   MOV     r0,#32                        ; 'space'
   STRB    r0,[r1,r3]                    ; store in block
   ADD     r3,r3,#1                      ; inc counter
   TEQ     r3,#32                        ; are we done ?
   BNE     $gmt_fill                     ; if not, continue
   MOV     r0,#0
   STRB    r0,[r1,r3]                    ; store as terminator
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

$`modenamebuf
   RES     32
$`gmt_none
   EQUZA   "None"

; parse for ticks or shading in the menu line
$parsefortick
   TEQ     r8,#0                         ; is there a window ?
   MOVEQ   r0,#%010                      ; if not, shade it
   BEQ     $pft_exit                     ; and exit
   MOV     r5,r0                         ; r2\5-> string
   LDRB    r0,[r8,#w_format]             ; read the mode we are in
 FNcall(Zap_ReadMode)                    ; get the mode we're in
   LDR     r2,[r1,#e_title*2]            ; read the current mode name
   REM     "%c04%c30Mode is for %$2, string= %$5"
   BL      $findmodevalidity
   MOVEQ   r0,#%000                      ; unshade it
   MOVNE   r0,#%010                      ; shade it
$pft_exit
   STR     r0,[sp]                       ; store as r0 for return
   LDMFD   (sp)!,{r0-r5,pc}              ; Return from call

; => r5-> string, r2->modename
; <= EQ if found, NE if not
;    r5-> string for filename
$findmodevalidity
   STMFD   (sp)!,{r0,r4,link}            ; Stack registers
   REM     "Find mode validity %$2 and %$5"
$fmv_findmodes
   LDRB    r0,[r5]                       ; read the first byte
   CMP     r0,#ASC("*")                  ; is it '*' ?
   ADDEQ   r5,r5,#1                      ; we'd better skip it !
   BEQ     $fmv_modevalid                ; if so, it must be valid!
   MOV     r4,#ASC("=")
   BL      comparestrings                ; check if we're in a valid mode
   BEQ     $fmv_modevalid                ; if valid, tick it
$fmv_skip
   LDRB    r0,[r5],#1                    ; read byte and inc
   REM     "Char to skip = %a0"
   CMP     r0,#32                        ; is it space ?
   BLT     $fmv_modeinvalid              ; if <, it's invalid
   BNE     $fmv_skip                     ; if not space, skip
   B       $fmv_findmodes                ;
$fmv_modevalid
   CMP     r0,r0                         ; it's valid
   LDMFD   (sp)!,{r0,r4,pc}              ; Return from call
$fmv_modeinvalid
   CMP     pc,#0                         ; it's not valid
   LDMFD   (sp)!,{r0,r4,pc}              ; Return from call


; *******************************************************************
; Subroutine:   comparestrings
; Description:  Compares two strings insensitively
; Parameters:   r2-> comparison string
;               r4 = terminator
;               r5-> list string
; Returns:      EQ if in the list, NE if not
;               r5-> terminator of string
; *******************************************************************
>comparestrings
   STMFD   (sp)!,{r0-r4,r6-r7,link}      ; Stack registers
$number_loop
   MOV     r7,#0                         ; start at the start of the string
   REM     "Comparing %$2 and %$5, terminator %r4"
$name_loop
   LDRB    r1,[r5,r7]                    ; read a char from list string
   REM     "Theirs %a1"
   CMP     r1,#ASC("a")                  ; is it >= 'a'
   RSBGES  r14,r1,#ASC("z")              ; check for being <='z'
   SUBGE   r1,r1,#32                     ; if so, make upper case !
   LDRB    r3,[r2,r7]                    ; read a char from our string
   REM     "Ours %a3"
   CMP     r3,#ASC("a")                  ; is it >= 'a'
   RSBGES  r14,r3,#ASC("z")              ; check for being <='z'
   SUBGE   r3,r3,#32                     ; if so, make upper case !

   CMP     r1,#32                        ; is it space (or lower) ?
   TEQGT   r1,r4                         ; is it a terminator
   MOVLTS  r1,#0                         ; if so, it's the end of the string
   CMPNE   r1,#ASC(",")                  ; is that their string done ?
   BNE     $notterminator                ; not a terminator
   CMP     r3,#32                        ; is ours ended too ?
   BGT     $findnext                     ; nope, so they don't
   B       $modevalid                    ; they are, so they match
$notterminator
   CMP     r3,r1                         ; are they the same ?
   ADDEQ   r7,r7,#1                      ; if so, next character
   BEQ     $name_loop                    ;        and go around again

; don't match, so skip to end of string
$findnext
   REM     "Finding next block, char = %a1"
   CMP     r1,r4                         ; is it terminator ?
   TEQNE   r1,#0                         ; or perhaps a 0 ?
   BEQ     $modeinvalid                  ; if so, done...
   CMP     r1,#32                        ; is that it ?
   BLE     $modeinvalid                  ; if so, it's FALSE
   CMP     r1,#ASC(",")                  ; is it , ?
   ADD     r7,r7,#1                      ; next one
   ADDEQ   r5,r5,r7                      ; if so, that's our offset
   BEQ     $number_loop                  ; check the next one
   LDRB    r1,[r5,r7]                    ; read a char from our string
   B       $findnext                     ; try the next character

$modevalid
   REM     "Returning TRUE"
   ADD     r5,r5,r7
   CMP     r0,r0                         ; return EQ
   LDMFD   (sp)!,{r0-r4,r6-r7,pc}        ; Return from call

$modeinvalid
   REM     "Returning FALSE"
   ADD     r5,r5,r7
   CMP     pc,#0                         ; return NE
   LDMFD   (sp)!,{r0-r4,r6-r7,pc}        ; Return from call

#Library "Strings",strlen.strcpy.getlinefromfile
#Library "Memory",claim.release
#Here Libraries

# POST
REM # RUN <CODE>
REM *RmKill Zap
REM *RmKill ZapJRF
# END

REM Call zap at entry offset a% Entry R0-R11=args R12=zap workspace
DEF FNcall(a%)
[OPT pass%
  LDR     r14,[r12]                 ; get start of zap table
  ADD     r14,r14,#a%               ; get address of sub
  STMFD   (sp)!,{r14}               ; save address on stack
  MOV     r14,pc                    ; return address (with flags)
  LDMFD   (sp)!,{pc}                ; call the sub
]:=""
